<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vuex原理探究</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  </head>

  <body>
    <div id="app">
      <div>{{msg}}</div>
      <div>我是state: {{$store.state.num}}</div>
      <div>我是getters doubleCounter: {{$store.getters.doubleCounter}}</div>
      <div>{{msg}}</div>
      <div>
        <button @click="add" style="margin: 20px 0">测试mutation</button>
      </div>
      <div><button @click="dispatch">测试actions</button></div>
    </div>
  </body>

  <script type="module">

    class Store {
      constructor(options) {
        // state响应式处理
        this._vm = new Vue({
          data: {
            $$state: options.state,
          },
        });

        this._mutations = options.mutations;
        this._actions = options.actions;
        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
        this.getters = {};
        options.getters && this.handleGetters(options.getters);
      }
      get state() {
        return this._vm._data.$$state;
      }
      set state(val) {
        console.error("Please user replaceState to reset state");
      }
      handleGetters(getters) {
        Object.keys(getters).map((key) => {
          Object.defineProperty(this.getters, key, {
            get: () => getters[key](this.state),
          });
        });
      }
      commit(type, payload) {
        const entry = this._mutations[type];
        if (!entry) {
          console.error("unknown mutation type");
        }
        entry(this.state, payload);
      }

      dispatch(type, payload) {
        const entry = this._actions[type];
        if (!entry) {
          console.error("unknown mutation type");
        }
        entry(this, payload);
      }
    }

    const install = function (vm) {
      vm.mixin({
        beforeCreate() {
          if (this.$options.store) {
            vm.prototype.$store = this.$options.store;
          }
        },
      });
    };

    let Vuex = { Store, install };
    Vue.use(Vuex);

    const store = new Vuex.Store({
      state: {
        num: 0,
      },
      mutations: {
        add(state) {
          state.num += 1;
        },
      },
      actions: {
        add({ commit }) {
          setTimeout((_) => {
            commit("add");
          }, 1000);
        },
      },
      modules: {},
      getters: {
        doubleCounter(state) {
          return state.num * 2;
        },
      },
    });

    new Vue({
      el: "#app",
      store,
      data: {
        msg: "Vuex原理探究",
      },
      methods: {
        add() {
          // console.log("我是$store", this.$store);
          this.$store.commit("add", 1);
        },
        dispatch() {
          this.$store.dispatch("add", 1);
        },
      },
    });
  </script>
</html>
